package com.example.demo.util;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.constant.QueryConstant;
import com.example.demo.common.constant.StatusBusiness;
import com.example.demo.common.exception.BusinessException;
import com.example.demo.util.gson.GsonUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author: wbx
 * @Description  ES工具类
 */
@Slf4j
@Component
public final class EsUtil {

    private static RestHighLevelClient restHighLevelClient;

    @Autowired
    public void setComponent(RestHighLevelClient restHighLevelClient) {
        EsUtil.restHighLevelClient = restHighLevelClient;
    }

    /**
     * 修改成功状态码
     */
    private static final int UPDATE = DocWriteResponse.Result.UPDATED.getOp();
    /**
     * 新增成功状态码
     */
    private static final int CREATED = DocWriteResponse.Result.CREATED.getOp();
    /**
     * 删除成功状态码
     */
    private static final int DELETED = DocWriteResponse.Result.DELETED.getOp();
    /**
     * es查询条件当前页数
     */
    private static final String PAGE = "page";
    /**
     * es查询条件每页行数
     */
    private static final String PAGE_SIZE = "pageSize";

    /**
     * 需要高亮显示的字段
     */
    private static final String HIGH_LIGHT = "highLight";

    /**
     * 验证索引是否存在
     */
    public static Boolean existsIndex(String indexName) {
        Boolean isExist = null;
        try {
            // 获取索引请求
            GetIndexRequest request = new GetIndexRequest(indexName);
            // 设置要查询的索引名称
            isExist = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
//            log.info("验证索引是否存在：{}", isExist);
            // 根据具体业务逻辑返回不同结果，这里为了方便直接将结果返回
        } catch (IOException e) {
//            log.error("", e);
        }
        return isExist;
    }

    /**
     * 创建索引
     */
    public static Boolean createIndex(String indexName) {
        Boolean isCreated = null;
        try {
            // 创建 Mapping
//            XContentBuilder mapping = XContentFactory.jsonBuilder()
//                    .startObject()
//                    .field("dynamic", true)
//                    .startObject("properties")
//                    .startObject("name")
//                    .field("type", "text")
//                    .startObject("fields")
//                    .startObject("keyword")
//                    .field("type", "keyword")
//                    .endObject()
//                    .endObject()
//                    .endObject()
//                    .startObject("address")
//                    .field("type", "text")
//                    .startObject("fields")
//                    .startObject("keyword")
//                    .field("type", "keyword")
//                    .endObject()
//                    .endObject()
//                    .endObject()
//                    .startObject("remark")
//                    .field("type", "text")
//                    .startObject("fields")
//                    .startObject("keyword")
//                    .field("type", "keyword")
//                    .endObject()
//                    .endObject()
//                    .endObject()
//                    .startObject("age")
//                    .field("type", "integer")
//                    .endObject()
//                    .startObject("salary")
//                    .field("type", "float")
//                    .endObject()
//                    .startObject("birthDate")
//                    .field("type", "date")
//                    .field("format", "yyyy-MM-dd")
//                    .endObject()
//                    .startObject("createTime")
//                    .field("type", "date")
//                    .endObject()
//                    .endObject()
//                    .endObject()
//                    // 字段名称
//                    .startObject("text")
//                    // 字段类型
//                    .field("type", "text")
//                    // 插入时分词
//                    .field("analyzer", "ik_smart")
//                    // 搜索时分词
//                    .field("search_analyzer", "ik_max_word")
//                    .endObject();
            // 创建索引配置信息，配置
            XContentBuilder builder  = XContentFactory.jsonBuilder();
            builder.startObject();
            {
                builder.startObject("properties");
                {
                    builder.startObject("id");
                    {
                        builder.field("type", "text");
                    }
                    builder.endObject();

                    builder.startObject("name");
                    {
                        builder.field("type", "text");
                    }
                    builder.endObject();

                    builder.startObject("age");
                    {
                        builder.field("type", "long");
                    }
                    builder.endObject();

                    builder.startObject("createTime");
                    {
                        builder.field("type", "date")
                                .field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
                                .field("ignore_malformed", false)
                                .field("null_value", "");
                    }
                    builder.endObject();

                    builder.startObject("text");
                    {
                        builder.field("type", "text")
                                // 插入时分词
                                .field("analyzer", "ik_smart")
                                // 搜索时分词
                                .field("search_analyzer", "ik_max_word");
                    }
                    builder.endObject();
                }
                builder.endObject();
            }
            builder.endObject();
            Settings settings = Settings.builder()
                    // 设置分片
                    .put("index.number_of_shards", 1)
                    // 设置副本
                    .put("index.number_of_replicas", 0)
                    .build();
            // 新建创建索引请求对象，然后设置索引类型（ES 7.0 将不存在索引类型）和 mapping 与 index 配置
            CreateIndexRequest request = new CreateIndexRequest(indexName);
            request.settings(settings);
            request.mapping(builder );
//            request.mapping("doc", mapping);
            // RestHighLevelClient 执行创建索引
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            // 判断是否创建成功
            isCreated = createIndexResponse.isAcknowledged();
//            log.info("是否创建成功：{}", isCreated);
            // 根据具体业务逻辑返回不同结果，这里为了方便直接将结果返回
        } catch (IOException e) {
            log.error("", e);
        }
        return isCreated;
    }

    /**
     * 删除索引
     */
    public static Boolean deleteIndex(String indexName) {
        Boolean siDeleted = null;
        try {
            // 新建删除索引请求对象
            DeleteIndexRequest request = new DeleteIndexRequest(indexName);
            // 执行删除索引
            AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            // 判断是否删除成功
            siDeleted = acknowledgedResponse.isAcknowledged();
//            log.info("是否删除成功：{}", siDeleted);
            // 根据具体业务逻辑返回不同结果，这里为了方便直接将结果返回
        } catch (IOException e) {
            log.error("", e);
        }
        return siDeleted;
    }

    /**
     *  往指定索引ES库中插入文档
     * @param indexName 索引名称
     * @param doc 文档名称
     * @return
     */
    public static boolean addDoc(String indexName, JSONObject doc) {
        boolean result = false;
        // 1.构建IndexRequest对象，用来描述ES发起请求的数据。
        IndexRequest indexRequest = new IndexRequest(indexName);
        // 2.设置文档ID。
        indexRequest.id(doc.getString("id"));
        // 3.使用IndexRequest.source方法设置文档数据，并设置请求的数据为JSON格式。
        indexRequest.source(doc.toString(), XContentType.JSON);
        // 4.使用ES High level client调用index方法发起请求，将一个文档添加到索引中。
        try {
            IndexResponse respose = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            if(CREATED == respose.getResult().ordinal()) {
                result = true;
                log.info("ES插入文档成功，{} == 索引是 {}", doc.toString(),indexName);
            } else if (UPDATE == respose.getResult().ordinal()) {
                throw new BusinessException(StatusBusiness.FAILD.getStatus(), "该id的文档已存在！");
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info("ES插入文档失败，{} == 索引是 {}", e.getMessage(), indexName);
        }
        return result;
    }

    /**
     * 根据ID删除文档
     * @param indexName 索引名称
     * @param id  id
     * @return
     */
    public static boolean deleteById(String indexName,String id) {
        boolean result = false;
        // 1.	构建delete请求
        DeleteRequest deleteRequest = new DeleteRequest(indexName, id);
        // 2.	使用RestHighLevelClient执行delete请求
        DeleteResponse response = null;
        try {
            response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            if(DELETED == response.getResult().ordinal()) {
                result = true;
            }
        } catch (Exception e) {
            if(e instanceof ElasticsearchException) {
                notFoundException(e, indexName, id);
            }
            e.printStackTrace();
        }
        return result;
    }


    /**
     *  文档 / 索引 不存在异常处理
     * @param e 异常类型
     * @param indexName 索引名称
     * @param id 文档id
     */
    public static void notFoundException(Exception e, String indexName, String id) {
        if (e instanceof ElasticsearchException) {
            ElasticsearchException esException = (ElasticsearchException) e;
            // 当捕获到文档id不存在异常
            if (esException.status().getStatus() == RestStatus.NOT_FOUND.getStatus()) {
                throw new BusinessException(StatusBusiness.FAILD.getStatus(), "该文档不存在，请检查！ 索引是：" + indexName + "，文档id是：" + id);
            }
        }
    }

    /**
     *  修改文档
     * @param indexName 索引名称
     * @param doc 文档json
     * @return
     */
    public static boolean updateDoc(String indexName, JSONObject doc) {
        boolean result = false;
        try {
            // 判断索引是否存在
            if(existsIndex(indexName)) {
                // 2.	构建UpdateRequest请求
                UpdateRequest updateRequest = new UpdateRequest(indexName, doc.getString("id"));
                // 3.	设置UpdateRequest的文档，并配置为JSON格式
                updateRequest.doc(doc.toString(), XContentType.JSON);
                // 4.	执行client发起update请求
                UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
                if (UPDATE == response.getResult().getOp()) {
                    result = true;
                }
            }
        } catch (Exception e) {
            if(e instanceof ElasticsearchException) {
                notFoundException(e, indexName, doc.getString("id"));
            }
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据索引名称和ID查询文档
     * @param indexName 索引名称
     * @param id 文档的ID
     * @return
     */
    public static JSONObject getDocByID(String indexName, String id) {
        JSONObject result = new JSONObject();
        // 创建GetRequest请求
        GetRequest getRequest = new GetRequest(indexName , id);
        // 是否获取源码内容
        getRequest.fetchSourceContext(new FetchSourceContext(true));
        try {
            // 发送GetRequest请求
            GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            // 获取响应数据并转换为jsonObject
            result = JSONObject.fromObject(response.getSourceAsString());
            result.put("id", id);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     *  根据条件查询指定索引下文档分页 current当前页 / size 每页条数
     * @param indexName 索引名称
     * @param jsonObject 对象
     */
    public static <T>IPage<T> searchPageByKeywords(String indexName, JSONObject jsonObject, Class<T> clz) {
        IPage<T> page = new Page<>();
        // 1.构建SearchRequest检索请求
        // 专门用来进行全文检索、关键字检索的API
        SearchRequest searchRequest = new SearchRequest(indexName);
        // 2.创建一个SearchSourceBuilder专门用于构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 开始页数
        searchSourceBuilder.from(Integer.parseInt(jsonObject.getString(PAGE)) - 1);
        // 每页条数
        searchSourceBuilder.size(Integer.parseInt(jsonObject.getString(PAGE_SIZE)));
        searchSourceBuilder.trackTotalHits(true);
        /*
          Bool查询对应Lucene中的BooleanQuery,它由一个或者多个子句组成,每个子句都有特定的类型

          must
          返回的文档必须满足must子句的条件,并且参与计算分值

          filter
          返回的文档必须满足filter子句的条件,但是不会像must一样,参与计算分值

          should
          返回的文档可能满足should子句的条件.在一个bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回.minimum_should_match参数定义了至少满足几个子句.

          must_not
          返回的文档必须不满足定义的条件

          如果一个查询既有filter又有should,那么至少包含一个should子句.

          bool查询也支持禁用协同计分选项disable_coord.一般计算分值的因素取决于所有的查询条件.

          bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值.
         */
        BoolQueryBuilder boolQueryBuilder  = QueryBuilders.boolQuery();

        // 3.使用QueryBuilders.multiMatchQuery构建一个查询条件（搜索title、jd），并配置到SearchSourceBuilder
        // MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd"); 精确查询
        // 模糊查询 匹配 test2为查询的字段名
//        WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("test2","*" + jsonObject.getString("test2") + "*");
        // 模糊查询 匹配 test2为查询的字段名
        boolQueryBuilder.filter(QueryBuilders.wildcardQuery("test2","*" + jsonObject.getString("test2") + "*"));

        // 将查询条件设置到查询请求构建器中
        searchSourceBuilder.query(boolQueryBuilder);

        // 4.调用SearchRequest.source将查询条件设置到检索请求
        searchRequest.source(searchSourceBuilder);

        // 5.执行RestHighLevelClient.search发起请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 总条数
        long total = searchResponse.getHits().getTotalHits().value;
        page.setTotal(total);
        SearchHit[] hitArray = searchResponse.getHits().getHits();
        // 6.遍历结果
        ArrayList<T> jobDetailArrayList = new ArrayList<>();
        for (SearchHit documentFields : hitArray) {
            // 1)获取命中的结果
            String json = documentFields.getSourceAsString();
            // 2)将JSON字符串转换为对象
            T jobDetail = GsonUtil.GsonToBean(json, clz);
            try {
                // 反射获取新生成对象的id属性
                Field id = clz.getDeclaredField("id");
                id.setAccessible(true);
                // 使用SearchHit.getId设置文档ID 给生成的对象赋值id为文档id
                id.set(jobDetail, documentFields.getId());
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
            jobDetailArrayList.add(jobDetail);
        }
        page.setRecords(jobDetailArrayList);
        page.setCurrent(Integer.parseInt(jsonObject.getString(PAGE)));
        page.setSize(Integer.parseInt(jsonObject.getString(PAGE_SIZE)));
        return page;
    }

    /**
     *  根据条件查询指定索引下文档列表
     * @param indexName 索引名称
     * @param jsonObject 对象
     */
    public static List<JSONObject> searchListByKeywords(String indexName, JSONObject jsonObject) {
        // 1.构建SearchRequest检索请求
        // 专门用来进行全文检索、关键字检索的API
        SearchRequest searchRequest = new SearchRequest(indexName);
        // 2.创建一个SearchSourceBuilder专门用于构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder  = QueryBuilders.boolQuery();

        // 3.使用QueryBuilders.multiMatchQuery构建一个查询条件（搜索title、jd），并配置到SearchSourceBuilder
        // MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd"); 精确查询
        // 模糊查询 匹配 test2为查询的字段名
//        WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("test2","*" + jsonObject.getString("test2") + "*");
        // 模糊查询 匹配 test2为查询的字段名
        boolQueryBuilder.filter(QueryBuilders.wildcardQuery("test2","*" + jsonObject.getString("test2") + "*"));

        // 将查询条件设置到查询请求构建器中
        searchSourceBuilder.query(boolQueryBuilder);

        // 4.调用SearchRequest.source将查询条件设置到检索请求
        searchRequest.source(searchSourceBuilder);

        // 5.执行RestHighLevelClient.search发起请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 总条数
        long total = searchResponse.getHits().getTotalHits().value;
        SearchHit[] hitArray = searchResponse.getHits().getHits();

        // 6.遍历结果
        ArrayList<JSONObject> jobDetailArrayList = new ArrayList<>();

        for (SearchHit documentFields : hitArray) {
            // 1)获取命中的结果
            String json = documentFields.getSourceAsString();

            // 2)将JSON字符串转换为对象
            JSONObject jobDetail = JSONObject.fromObject(json);

            // 3)使用SearchHit.getId设置文档ID
            jobDetail.put("id", documentFields.getId());

            jobDetailArrayList.add(jobDetail);
        }
        return jobDetailArrayList;
    }


    /**
     *  根据条件查询指定索引下文档高亮分页 current当前页 / size 每页条数
     * @param indexName 索引名称
     * @param jsonObject 对象
     */
    public static <T>IPage<T> searchHighLightPageByKeywords(String indexName, JSONObject jsonObject, Class<T> clz) {
        IPage<T> page = new Page<>();
        // 1.构建SearchRequest检索请求
        // 专门用来进行全文检索、关键字检索的API
        SearchRequest searchRequest = new SearchRequest(indexName);
        // 2.创建一个SearchSourceBuilder专门用于构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 开始页数
        searchSourceBuilder.from(Integer.parseInt(jsonObject.getString(PAGE)) - 1);
        // 每页条数
        searchSourceBuilder.size(Integer.parseInt(jsonObject.getString(PAGE_SIZE)));
        searchSourceBuilder.trackTotalHits(true);
        // 高亮配置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        // 高亮字段
        highlightBuilder.field(QueryConstant.HIGH_LIGHT_FIELD);
        // 高亮标签
        highlightBuilder.preTags("<font color='red'>");
        highlightBuilder.postTags("</font>");
        // 请求设置高亮
        searchSourceBuilder.highlighter(highlightBuilder);
        /*
          Bool查询对应Lucene中的BooleanQuery,它由一个或者多个子句组成,每个子句都有特定的类型

          must
          返回的文档必须满足must子句的条件,并且参与计算分值

          filter
          返回的文档必须满足filter子句的条件,但是不会像must一样,参与计算分值

          should
          返回的文档可能满足should子句的条件.在一个bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回.minimum_should_match参数定义了至少满足几个子句.

          must_not
          返回的文档必须不满足定义的条件

          如果一个查询既有filter又有should,那么至少包含一个should子句.

          bool查询也支持禁用协同计分选项disable_coord.一般计算分值的因素取决于所有的查询条件.

          bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值.
         */
        BoolQueryBuilder boolQueryBuilder  = QueryBuilders.boolQuery();
        // 3.使用QueryBuilders.multiMatchQuery构建一个查询条件（搜索title、jd），并配置到SearchSourceBuilder
        // MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd"); 精确查询
        // 模糊查询 匹配 test2为查询的字段名
//        WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("test2","*" + jsonObject.getString("test2") + "*");

        // 是否需要模糊查询
        if(jsonObject.containsKey(QueryConstant.WILDCARD_QUERY)) {
            // 模糊查询 匹配 test2为查询的字段名  查询字段名 / 查询字段名名传入的值
            boolQueryBuilder.filter(QueryBuilders.wildcardQuery("test2", "*" + jsonObject.getString("test2") + "*"));
        }
        // 查询时间范围 gte 大于等于  / lte 小于等于
        if(jsonObject.containsKey(QueryConstant.createTime)) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery(QueryConstant.createTime).gte(jsonObject.getString(QueryConstant.createTime)));
            // 查询后排序
            searchSourceBuilder.sort(new FieldSortBuilder(QueryConstant.createTime).order(SortOrder.DESC));
        }

        // 是否需要进行分词查询
        if(jsonObject.containsKey(QueryConstant.MATCH)) {
            boolQueryBuilder.must(QueryBuilders.boolQuery()
                            // 进行分词查询  匹配bt时评分最高，nr、twnr和hfnr评分次之，评分越高的查询时排序越靠前
                    .should(QueryBuilders.matchQuery(jsonObject.getString(QueryConstant.MATCH), jsonObject.getString(QueryConstant.searchText)))
//                    .should(QueryBuilders.multiMatchQuery(jsonObject.getString(QueryConstant.searchText), "nr", "twnr", "hfnr").boost(0.01f))
            );
        }
//
        // 将查询条件设置到查询请求构建器中
        searchSourceBuilder.query(boolQueryBuilder);

        // 4.调用SearchRequest.source将查询条件设置到检索请求
        searchRequest.source(searchSourceBuilder);

        // 5.执行RestHighLevelClient.search发起请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 总条数
        long total = searchResponse.getHits().getTotalHits().value;
        page.setTotal(total);
        SearchHit[] hitArray = searchResponse.getHits().getHits();

        // 6.遍历结果
        ArrayList<T> jobDetailArrayList = new ArrayList<>();

        for (SearchHit documentFields : hitArray) {
            // 1)获取命中的结果
            String json = documentFields.getSourceAsString();

            // 2)将JSON字符串转换为对象
            T jobDetail = GsonUtil.GsonToBean(json, clz);

            // 3)使用SearchHit.getId设置文档ID
            try {
                // 反射获取新生成对象的id属性
                Field id = clz.getDeclaredField("id");
                Field highLight = clz.getDeclaredField(HIGH_LIGHT);
                id.setAccessible(true);
                highLight.setAccessible(true);
                // 使用SearchHit.getId设置文档ID 给生成的对象赋值id为文档id
                id.set(jobDetail, documentFields.getId());
                Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
                // 需要进行高亮拼接的字段为
                HighlightField hightLightField = highlightFields.get(jsonObject.getString(QueryConstant.HIGH_LIGHT_TEXT));
                if(hightLightField != null) {
                    // 获取指定字段的高亮片段
                    Text[] fragments = hightLightField.getFragments();
                    // 将这些高亮片段拼接成一个完整的高亮字段
                    StringBuilder builder = new StringBuilder();
                    for(Text text : fragments) {
                        builder.append(text);
                    }
                    // 高亮字段设置到实体类中
                    highLight.set(jobDetail, builder.toString());
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
            jobDetailArrayList.add(jobDetail);
        }
        page.setRecords(jobDetailArrayList);
        page.setCurrent(Integer.parseInt(jsonObject.getString(PAGE)));
        page.setSize(Integer.parseInt(jsonObject.getString(PAGE_SIZE)));
        return page;
    }















    /**
     * @return json数据 数据不存在返回""
     * @Author wbx
     * @Description 根据索引和ID获取数据
     * @Date 15:59 2020/5/19
     * @Param [indexName, id]
     **/
    public static String getDataByIndexAndId(String indexName, String id) {
        try {
            // 获取请求对象
            GetRequest getRequest = new GetRequest(indexName,  id);
            // 是否获取源码内容
            getRequest.fetchSourceContext(new FetchSourceContext(true));
            // 获取数据信息
            GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            // 存在就返回json
            if (getResponse.isExists()) {
                return getResponse.getSourceAsString();
            }
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * @return true 存在 fasle 不存在
     * @Author wbx
     * @Description 根据索引和ID验证数据是否存在
     * @Date 15:59 2020/5/19
     * @Param [indexName, id]
     **/
    public static Boolean existDataByIndexAndId(String indexName, String id) {
        try {
            // 获取请求对象
            GetRequest getRequest = new GetRequest(indexName,  id);
            // 是否获取源码内容
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            // 执行请求，验证文档是否存在
            return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * @return
     * @Author wbx
     * @Description 根据索引和ID更新数据
     * @Date 15:59 2020/5/19
     * @Param [indexName, id,jsonData]
     **/
    public static Boolean updateDataByIndexAndId(String indexName, String id, JSONObject jsonData) {
        try {
            // 创建索引请求对象
            UpdateRequest updateRequest = new UpdateRequest(indexName,  id);
            // 设置更新文档内容
            updateRequest.doc(jsonData, XContentType.JSON);
            // 执行更新文档
            UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
            // 根据具体业务逻辑返回不同结果，这里为了方便直接将结果返回
            String result = String.valueOf(response.getResult());
            if ("NOOP".equals(result)) {
                //无需修改，值相同 NOOP
                return true;
            } else if ("UPDATED".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    /**
     * @return
     * @Author wbx
     * @Description 根据索引和ID删除数据
     * @Date 15:59 2020/5/19
     * @Param [indexName, id]
     **/
    public static Boolean deleteDocument(String indexName, String id) {
        try {
            // 创建删除请求对象
            DeleteRequest deleteRequest = new DeleteRequest(indexName,  id);
            // 执行删除文档
            DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            // 根据具体业务逻辑返回不同结果，这里为了方便直接将结果返回
            String result = String.valueOf(response.getResult());
            if ("NOT_FOUND".equals(result)) {
                //NOT_FOUND 不存在
                return true;
            } else if ("DELETED".equals(result)) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }
}
